/*
 * cpe.c
 * 
 * Copyright 2013 Raju <raju39k@gmail.com>
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301, USA.
 * 
 * 
 */

/**
 * @file cpe.c
 * @author Reddeiah Raju Konduru (raju39k@gmail.com)
 * @date   July, 2013
 * @brief  This is the main function of cpe program.
 * This is the program for handling all other cpe operations.
 */
 
/******************************************************************************
 *                              HEADER FILE INCLUSIONS                        *
 ******************************************************************************/
 
 #include "cpe.h"


/******************************************************************************
 *                                 MACRO DEFINITIONS                          *
 ******************************************************************************/
/**
 * Help string for the main program
 */
 #define HELP  "\t===========================================================\n" \
               "\tNOTE: For Log file check the below paths\n\n"                  \
               "\t      1. Errors logs            --  /var/log/error.log\n"      \
               "\t      2. Function Tracing logs  --  /var/log/func_trace.log\n" \
               "\t      3. RPC Call Tracing logs  --  /var/log/rpc_trace.log\n"  \
               "\t===========================================================\n"
  
/**
 * Macros for File Paths
 */
 #define ERROR_LOG_FILE  "/var/log/cpe/error.log"       /**< Error Log file path */
 #define FUNC_TRACE_FILE "/var/log/cpe/func_trace.log"  /**< Function Trace Log file path */
 #define RPC_TRACE_FILE  "/var/log/cpe/rpc_trace.log"   /**< RPC Trace Log file path */
 #define CPE_CONFIG_FILE "config/cpe_config.cfg"        /**< CPE Configuration file */

/******************************************************************************
 *                                 GLOBAL VARIABLES                           *
 ******************************************************************************/
 
 FILE *error_fp = NULL;           /**< Error log file handler */
 FILE *func_trace_fp = NULL;      /**< Trace log file handler */
 FILE *rpc_trace_fp = NULL;       /**< RPC log file handler */

/**
 * Array of Fault_String Structures - Fault strings, Fault Types
 * @todo change if required in future as per New Amendment
 *  */
/* TODO: chane if required in future as per New Amendment */
 FaultCodeStruct_t Fault_List[NUM_OF_FAULT_CODES] = {
      { "Method not supported", "Server" },
      { "Request denied", "Server" },
      { "Internal error", "Server" },
      { "Invalid arguments", "Client" },
      { "Resources exceeded", "Server" },
      { "Invalid Parameter name", "Client" },
      { "Invalid Parameter type", "Client" },
      { "Invalid Parameter value", "Client" },
      { "Attempt to set a non-writable Parameter", "Client" },
      { "Notification request rejected", "Server" },
      { "File transfer failure", "Server" },
      { "Upload failure", "Server" },
      { "File transfer server authentication failure", "Server" },
      { "Unsupported protocol for file transfer", "Server" },
      { "File transfer failure: unable to join multicast group", "Server" },
      { "File transfer failure: unable to contact file server", "Server" },
      { "File transfer failure: unable to access file", "Server" },
      { "File transfer failure: unable to complete download", "Server" },
      { "File transfer failure: file corrupted or otherwise unusable", "Server" },
      { "File transfer failure: file authentication failure", "Server" },
      { "File transfer failure: unable to complete download within specified time windows", "Client" },
      { "Cancelation of file transfer not permitted in current transfer state", "Client" },
      { "Invalid UUID Format", "Server" },
      { "Unknown Execution Environment", "Server" },
      { "Disabled Execution Environment", "Server" },
      { "Deployment Unit to Execution Environment Mismatch", "Server" },
      { "Duplicate Deployment Unit", "Server" },
      { "System Resources Exceeded", "Server" },
      { "Unknown Deployment Unit", "Server" },
      { "Invalid Deployment Unit State", "Server" },
      { "Invalid Deployment Unit Update – Downgrade not permitted", "Server" },
      { "Invalid Deployment Unit Update – Version not specified", "Server" },
      { "Invalid Deployment Unit Update – Version already exists", "Server" }
 };

 
/******************************************************************************
 *                           STATIC FUNCTION PROTOTYPES                       *
 ******************************************************************************/
 static int open_log_files(void);                          /**< open log files */
 static int load_cpe_config(CWMPDetails_t *cwmp_data);     /**< load cpe details */
 static int validate_config_file(CWMPDetails_t *cwmp_data);/**< validate cpe config */
 static void parse_url(CWMPDetails_t *cwmp_data);          /**< parse cpe ans acs url's */



/******************************************************************************
 *                               FUNCTION DEFINITIONS                         *
 ******************************************************************************/

/**
 * @brief This function will close file handlers and free memory
 * Description:
 * 
 *         a. Check for opened file handlers and close them
 * 
 *         b. Check for allocated memory and free it
 * 
 *         c. After above two actions exit the program
 * 
 * @param[in] ret_value Return value of the program to exit
 * 
 * @return Return type is void
 */
/* Function to close all file handler's and free allocated memory */
 void cpe_exit(int ret_value)
 {
       PRINT_FUNC_TRACE(ENTERED)

       /* Close the opened file handlers */
       fcloseall();

       /*TODO: free all the buffers used here */ 
       /* Template to free the memory allocated buffers
       if (example_buff != NULL) {
             (void )free(example_buff);
       }
       */

       PRINT_FUNC_TRACE(EXITED)
       exit(ret_value);
 }


/**
 * @brief This function will open file handlers of log files
 * Description:
 * 
 *         a. Open the file handler of error log file
 * 
 *         b. Open the file handler of function trace log file
 * 
 *         c. Open the file handler of rpc trace log file
 * 
 * @return CPE_OK on success otherwise CPE_ERR
 */
/* Function to open log files */
 static int open_log_files(void)
 {
       /* open the file handler of error log file */
       if ((error_fp = fopen(ERROR_LOG_FILE, "a")) == NULL) {
             fprintf(stderr, "Unable to open log file (%s)\n", ERROR_LOG_FILE);
             return CPE_ERR;
       }
       
       /* open the file handler of function trace log file */
       if ((func_trace_fp = fopen(FUNC_TRACE_FILE, "a")) == NULL) {
             PRINT_ERROR("Unable to open Function Trace log file (%s)\n", FUNC_TRACE_FILE)
             return CPE_ERR;
       }
       
       /* open the file handler of rpc trace log file */
       if ((rpc_trace_fp = fopen(RPC_TRACE_FILE, "a")) == NULL) {
             PRINT_ERROR("Unable to open RPC Calls Trace log file (%s)\n", RPC_TRACE_FILE)
             return CPE_ERR;
       }

       return CPE_OK;
}


/**
 * @brief This function will parse the url
 * Description:
 * 
 *         a. Parse the cpe url and fill cpe protocol, ip, port and path
 * 
 *         b. Parse the acs url and fill acs protocol, ip, port and path
 * 
 * @return void return type
 */
/* Function to parse the cpe url and acs url */
 static void parse_url(CWMPDetails_t *cwmp_data)
 {
       PRINT_FUNC_TRACE(ENTERED)

       char url[64];              /**< Temporary buff to store url of cpe and acs */
       char *save_ptr = NULL;     /**< Remaining string of strtok_r function call */
       
       strcpy(url, cwmp_data->cpe.url);

       /* parse cpe url and fill the structure (protocol://ip:port/path) */
       strcpy(cwmp_data->cpe.protocol, strtok_r(cwmp_data->cpe.url, ":", &save_ptr));
       strcpy(cwmp_data->cpe.ip, strtok_r(NULL, ":", &save_ptr) + 2);
       cwmp_data->cpe.port = atoi(strtok_r(NULL, "/", &save_ptr));
       strcpy(cwmp_data->cpe.path, save_ptr);

       strcpy(url, cwmp_data->acs.url);

       /* parse acs url and fill the structure (protocol://ip:port/path) */
       strcpy(cwmp_data->acs.protocol, strtok_r(cwmp_data->acs.url, ":", &save_ptr));
       strcpy(cwmp_data->acs.ip, strtok_r(NULL, ":", &save_ptr) + 2);
       cwmp_data->acs.port = atoi(strtok_r(NULL, "/", &save_ptr));
       strcpy(cwmp_data->acs.path, save_ptr);

       PRINT_FUNC_TRACE(EXITED)

       return ;
 }


/**
 * @brief This function will validate loaded configuration data
 * Description:
 * 
 *         a. Check for lemons
 * 
 *         b. Validate all fields in CWMPDetails structure
 * 
 * @param[in,out] cwmp_data Structure for parsing url filling cwmp details
 *
 * @return CPE_OK on success otherwise CPE_ERR
 */
/* Function to validate the cpe config file */
 static int validate_config_file(CWMPDetails_t *cwmp_data)
 {
       PRINT_FUNC_TRACE(ENTERED)

       if (cwmp_data->cpe.url[0] == '\0') {
             PRINT_ERROR("CPE connection Request url not found\n")
             return CPE_ERR;
       }
       
       if (cwmp_data->acs.url[0] == '\0') {
             PRINT_ERROR("ACS url not found\n")
             return CPE_ERR;
       }
       
       parse_url(cwmp_data);

       /* validate cpe details (protocol, ip address, and port number) */
       if (cwmp_data->cpe.protocol[0] == '\0' || cwmp_data->cpe.ip[0] == '\0' ||
             cwmp_data->cpe.port == 0) {
             PRINT_ERROR("CPE Protocol, IP address, and Port number are compulsory\n")
             return CPE_ERR;
       }

       /* validate acs details (protocol, ip address, and port number) */
       if (cwmp_data->acs.protocol[0] == '\0' || cwmp_data->acs.ip[0] == '\0' ||
             cwmp_data->acs.port == 0) {
             PRINT_ERROR("ACS Protocol, IP address, and Port number are compulsory\n")
             return CPE_ERR;
       }

       PRINT_FUNC_TRACE(EXITED)
       return CPE_OK;
 }


/**
 * @brief This function will load the cpe details from configuration file
 * Description:
 * 
 *         a. Check for lemons
 * 
 *         b. Open the configuration file
 * 
 *         c. Copy config data to structure
 * 
 * @param[in,out] cwmp_data Structure for filling cpe details
 *
 * @return CPE_OK on success otherwise CPE_ERR
 */
/* Function to load cpe configuration file */
 static int load_cpe_config(CWMPDetails_t *cwmp_data)
 {
       PRINT_FUNC_TRACE(ENTERED)

       FILE *config_fp;           /**< cpe config file file handler */

       /* open the config file for reading cpe details */
       if ((config_fp = fopen(CPE_CONFIG_FILE, "r")) == NULL) {
             PRINT_ERROR("Unable to open cpe configuration file (%s)\n", CPE_CONFIG_FILE)
             return CPE_ERR;
       }

       char buffer[BUFF_SIZE];    /**< Storing the contents from config file */
       char *token = NULL;        /**< Return value of STRTOK function call*/
       char *save_ptr = NULL;     /**< Remaing string from STRTOK function call*/

       /* Setting the buffer to null caharcters */
       memset(buffer, '\0', BUFF_SIZE);

       /* cpe config file parsing */
       while (!feof(config_fp)) {

             /* Read the entire line */
             if (!fgets(buffer, BUFF_SIZE, config_fp))
                   continue;

             buffer[strlen(buffer) - 1] = '\0';

             /* if the line is commenting or blank ignore it */
             if ((buffer[0] != 'C') && (buffer[0] != 'A') && (buffer[0] != 'E'))
                   continue;

             /* get the parameter name and value */
             if ((token = strtok_r(buffer," ", &save_ptr)) == NULL)
                   continue;

             /* seperate parameter name and value */
             if ((token = strtok_r(token,"=", &save_ptr)) == NULL)
                   continue;

             /* if the line is EVENT copy value */
             if (token[0] == 'E') {
                   strcpy(cwmp_data->events, save_ptr);
  
             /* if the line is URL and check for CPE or ACS and store it */
             } else if (!strcmp(token + 4, "URL")) {
                   if (token[0] == 'C')
                         strcpy(cwmp_data->cpe.url, save_ptr);
                   else if (token[0] == 'A')
                         strcpy(cwmp_data->acs.url, save_ptr);

             /* if the line is USERNAME and check for CPE or ACS and store it */
             } else if (!strcmp(token + 4, "USERNAME")) {
                   if (token[0] == 'C')
                         strcpy(cwmp_data->cpe.username, save_ptr);
                   else if (token[0] == 'A')
                         strcpy(cwmp_data->acs.username, save_ptr); 

             /* if the line is PASSWORD and check for CPE or ACS and store it */
             } else if (!strcmp(token + 4, "PASSWORD")) {
                   if (token[0] == 'C')
                         strcpy(cwmp_data->cpe.password, save_ptr);
                   else if (token[0] == 'A') 
                         strcpy(cwmp_data->acs.password, save_ptr);

             /* if the line is CPE_PERIODIC_ENABLE and check for CPE or ACS and store it */
             } else if (!strcmp(token , "CPE_PERIODIC_ENABLE")) {
                   cwmp_data->cpe.periodic_enable = atoi(save_ptr);

             /* if the line is CPE_PERIODIC_INTERVAL store it */
             } else if (!strcmp(token , "CPE_PERIODIC_INTERVAL")) {
                   cwmp_data->cpe.periodic_interval = atoi(save_ptr);
             }
       }

       if (validate_config_file(cwmp_data) != CPE_OK)
             return CPE_ERR;

       /* close cpe config file handler */
       fclose(config_fp);

       PRINT_FUNC_TRACE(EXITED)
       return CPE_OK;
 }


/**
 * @brief This is the Main function of TR-069 Client
 * Description:
 * 
 *         a. Parse the Command line arguments
 * 
 *         b. Create one thread for accepting connection from ACS
 * 
 *         c. Call the sub modeules ( Inform, GetRPCMethods etc.)
 * 
 * @param[in] argc Number of Commandline Arguments
 * @param[in] argv A pointer to the list of commandline arguments
 * 
 * @return CPE_OK on Success and CPE_ERR on failure. 
 */
/* Main program of CPE */ 
 int main(int argc, char **argv)
 {
       /** This program should be run as Super User */
       if (getuid() != 0) {
             fprintf(stderr, "%s should be run in Super User\n", argv[0]);
             exit(CPE_ERR);
       }

       /* print help to show available options to user */
       fprintf(stderr, HELP);

       if (open_log_files() != CPE_OK) {
             cpe_exit(CPE_ERR);
       }

       PRINT_FUNC_TRACE(ENTERED)

       CWMPDetails_t cwmp_data;     /**< cpe_data to store CPE basic details */

       /* Setting the buffer to null caharcters */
       memset(&cwmp_data, '\0', sizeof(CWMPDetails_t));

       /* Setting the periodic enable and periodic interval to -1 */
       cwmp_data.cpe.periodic_enable = -1;
       cwmp_data.cpe.periodic_interval = -1;

       if (load_cpe_config(&cwmp_data) != CPE_OK) {
             cpe_exit(CPE_ERR);
       }

       PRINT_FUNC_TRACE(ENTERED)

       cpe_exit(CPE_OK);

       return CPE_OK;
 }
